/**
 * Copyright 2009 University of Washington This code is released under the GPL version 3 - with a copy of the license
 * included
 * 
 * @author Maya Hao Li
 */
package seedpod.rdb;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Vector;

import seedpod.exceptions.SeedpodException;
import seedpod.kb2db.ClsMap;
import seedpod.kb2db.ModelMap;
import seedpod.model.NoObjectFoundException;
import seedpod.model.SeedpodDO;
import seedpod.model.SeedpodObjectFactory;
import seedpod.model.Thing;
import seedpod.model.rdb.Relation;

public class PManager {
	private RdbConnection _connection = null;
	private ModelMap _map = null;
	private SeedpodObjectFactory _objectFactory = SeedpodObjectFactory.getInstance();

	public PManager() {}

	public SeedpodDO createObject( Integer oid ) throws NoObjectFoundException {
		String clsType = QueryFactory.getObjectType( oid );
		if ( clsType == null ) throw new NoObjectFoundException();
		System.err.println( "PManager.createobject " + oid + " found type " + clsType );
		return getObject( clsType, oid );
	}

	public SeedpodDO createObject( String classType ) {
		return getObject( classType );
	}

	public SeedpodDO createObject( String classType, Integer oid ) {
		System.err.println( "PManager.createobject " + oid + " type " + classType );
		SeedpodDO obj = getObject( classType, oid );
		return obj;
	}

	/**
	 * This will actually get a new oid from the database. this object will be transient because it's not saved to the
	 * database table correlated with classtype.
	 * 
	 * @param classType
	 * @return
	 */
	public SeedpodDO createObjectWithId( String classType ) {

		return null;
	}

	public void deletePersistent( SeedpodDO object ) throws SeedpodException {
		object.delete();
	}

	protected SeedpodDO getDefaultSeedpodDO( String classType, Integer oid, PManager manager ) {
		try {
			return new SeedpodDO( classType, oid, this );
		} catch (NoObjectFoundException e) {
			return null;

		}
	}

	public ModelMap getModelMap() {
		return _map;
	}

	protected SeedpodDO getObject( Integer oid ) throws NoObjectFoundException {
		SeedpodDO obj = null;

		String classType = QueryFactory.getObjectType( oid );
		if ( classType != null ) obj = getObject( classType, oid );
		return obj;
	}

	protected SeedpodDO getObject( String classType ) {
		return getObject( classType, new Integer( Thing.UNKNOWN_OID ) );
	}

	protected SeedpodDO getObject( String classType, Integer oid ) {
		Object obj = null;

		Class<?> cls = _objectFactory.getImplJavaClass( classType );
		Class<?>[] argCls = new Class[3];
		Object[] initArgs = new Object[] { classType, new Integer( oid ), this };

		try {
			// initialize the constructor argument class types
			argCls[0] = Class.forName( "java.lang.String" );
			argCls[1] = Integer.class;
			argCls[2] = PManager.class;

			System.err.println( "******** start getting constructor in getObject " + cls.getName() );
			Constructor<?> constructor = cls.getConstructor( argCls );
			obj = constructor.newInstance( initArgs );
		} catch (InstantiationException instanceE) {
			instanceE.printStackTrace();
			return getDefaultSeedpodDO( classType, oid, this );
		} catch (IllegalAccessException iae) {
			iae.printStackTrace();
			return getDefaultSeedpodDO( classType, oid, this );
		} catch (InvocationTargetException rite) {
			rite.printStackTrace();
			return getDefaultSeedpodDO( classType, oid, this );
		} catch (NoSuchMethodException nsme) {
			nsme.printStackTrace();
			return getDefaultSeedpodDO( classType, oid, this );
		} catch (ClassNotFoundException cnfe) {
			cnfe.printStackTrace();
			return getDefaultSeedpodDO( classType, oid, this );
		}

		return obj != null ? (SeedpodDO)obj : getDefaultSeedpodDO( classType, oid, this );
	}

	public RdbConnection getRdbConnection() {
		return _connection;
	}

	public void makePersistent( PersistenceDO object ) throws SeedpodException {
		if ( object.isDirty() ) object.makePersist();
	}

	public void makePersistent( Vector<PersistenceDO> objects ) throws SeedpodException {
		for ( int i = 0; i < objects.size(); i++ )
			objects.elementAt( i ).makePersist();
	}

	public ArrayList<SeedpodDO> query( String classType, String filter ) throws SeedpodException {
		ClsMap cmap = _map.getClsMap( classType );
		StringBuffer sql = RdbSql.selectSql( cmap.getRelationName(), null, filter );
		ResultSet rs;
		ArrayList<SeedpodDO> results = new ArrayList<SeedpodDO>();
		Integer oid;
		SeedpodDO obj;

		// TODO not sure why some of the Primary key in the meta-table is :THING
		boolean hasPK = cmap.getPrimaryKey() != null && cmap.getPrimaryKey().equalsIgnoreCase(
				Relation.DEFAULT_PRIMARY_KEY );

		try {
			rs = getRdbConnection().executeQuery( sql.toString() );

			while (rs.next()) {
				if ( hasPK ) {
					oid = new Integer( rs.getInt( Relation.DEFAULT_PRIMARY_KEY ) );
					obj = createObject( classType, oid );
					obj.load( rs );
				} else {
					obj = createObject( classType );
					obj.makePersist();
					obj.load( rs );
				}
				results.add( obj );
			}
		} catch (SQLException e) {
			throw new SeedpodException( e, "Problems occurred running query " + sql.toString() );
		}
		return results;
	}

	public Collection<SeedpodDO> queryAll( String classType ) throws SeedpodException {
		return query( classType, null );
	}

	public ArrayList<SeedpodDO> queryView( String classType, String filter ) throws SeedpodException {
		ClsMap cmap = _map.getClsMap( classType );
		StringBuffer sql = RdbSql.selectSql( cmap.getViewName(), null, filter );
		ResultSet rs;
		ArrayList<SeedpodDO> results = new ArrayList<SeedpodDO>();
		Integer oid;
		SeedpodDO obj;

		boolean hasPK = cmap.getPrimaryKey() != null && cmap.getPrimaryKey().equalsIgnoreCase(
				Relation.DEFAULT_PRIMARY_KEY );

		try {
			rs = getRdbConnection().executeQuery( sql.toString() );

			while (rs.next()) {
				if ( hasPK ) {
					oid = new Integer( rs.getInt( Relation.DEFAULT_PRIMARY_KEY ) );
					obj = createObject( classType, oid );
					obj.load( rs );
				} else {
					obj = createObject( classType );
					// obj.makePersist();
					obj.load( rs );
				}
				results.add( obj );
			}
		} catch (SQLException e) {
			e.printStackTrace();
			throw new SeedpodException( e, "Problems occurred running query " + sql.toString() );
		}
		return results;
	}

	public void setModelMap( ModelMap mm ) {
		_map = mm;
	}

	public void setRdbConnection( RdbConnection connection ) {
		_connection = connection;
	}

	public void setRdbConnection( RdbConnectParameters param ) {
		_connection = RdbConnection.instance();
		_connection.setConnectionParameters( param );
	}

}
